/*
 * Copyright 2011 The Rusted Roof, LLC.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package org.proteusframework.api.base;

import org.proteusframework.api.model.*;
import org.proteusframework.util.Assert;

import java.util.*;

/**
 * Base class for a plugin archetype's {@link IVersionCollection} definition.
 *
 * @author Tacoma Four
 */
public abstract class AbstractVersionCollection implements IVersionCollection
{
    /**
     * Master collection of all known plugin API versions.
     */
    private final Collection<IVersion> versionList = new ArrayList<IVersion>();

    /**
     * Map of versioned interface keyed by version identifier.
     */
    protected final Map<IVersion, VersionedInterface> versionedInterfaceMap = new HashMap<IVersion, VersionedInterface>();

    /**
     * Map of which plugin API versions are supported by a given plugin.
     */
    protected final Map<IQualifiedName, Collection<IVersion>> implMap = new HashMap<IQualifiedName, Collection<IVersion>>();

    /**
     * Collection of properties across the complete version collection.
     */
    protected final Map<String, String> propertiesMap = new HashMap<String, String>();

    /**
     * Constructs a version collection consisting of the declared versioned interface definitions.
     *
     * @param versions Versioned interface definitions
     */
    protected AbstractVersionCollection(VersionedInterface... versions)
    {
        Assert.parameterNotNull(versions, "Parameter 'versions' must not be null");

        for (VersionedInterface vi : versions)
        {
            versionList.add(vi.getVersion());
            versionedInterfaceMap.put(vi.getVersion(), vi);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Map<String, String> getProperties()
    {
        Map<String, String> clonedMap = new HashMap<String, String>();

        for (String key : propertiesMap.keySet())
            clonedMap.put(key, propertiesMap.get(key));

        return clonedMap;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String getProperty(String key)
    {
        return propertiesMap.get(key);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean hasProperty(String key)
    {
        return propertiesMap.containsKey(key);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean isVersionInstalled(IVersion version)
    {
        return versionList.contains(version);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Collection<IVersion> supportedVersions()
    {
        return versionList;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public VersionedInterface getInterface(IVersion version) throws NoSuchVersionException
    {
        VersionedInterface versionedInterface = null;
        if (isVersionInstalled(version))
        {
            versionedInterface = versionedInterfaceMap.get(version);
        } else
        {
            throw new NoSuchVersionException(version, "No matching interface version exists in the registrar");
        }

        return versionedInterface;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Collection<VersionedInterface> getInterfaces()
    {
        return Collections.unmodifiableCollection(versionedInterfaceMap.values());
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void registerVersion(IQualifiedName qualifiedName, Collection<IVersion> versions)
    {
        versionList.addAll(versions);
        implMap.put(qualifiedName, versions);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public IQualifiedName getInterfaceQualifiedName(IVersion version) throws NoSuchVersionException
    {
        IQualifiedName match = null;

        for (IQualifiedName qn : implMap.keySet())
        {
            for (IVersion curVersion : implMap.get(qn))
            {
                if (curVersion.equals(version))
                {
                    match = qn;
                    break;
                }
            }

            if (null != match)
                break;
        }

        if (null == match)
            throw new NoSuchVersionException(version, "No qualified name is mapped to the given version");

        return match;
    }
}
